#region License

//
// Author: Javier Lozano <javier@lozanotek.com>
// Copyright (c) 2009-2010, lozanotek, inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#endregion

namespace MvcTurbine.Web {
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;
    using Blades;
    using ComponentModel;
    using MvcTurbine.Blades;
    using Routing;

    /// <summary>
    /// Defines the default flow of a <see cref="TurbineApplication"/> instance.
    /// </summary>
    [Serializable]
    public class RotorContext : IRotorContext {
        /// <summary>
        /// Default constructor
        /// </summary>
        public RotorContext(ITurbineApplication application) {
            Application = application;
            AutoRegistrations = new AutoRegistrationList();

            application.Initialized += Initialize;
        }

        /// <summary>
        /// Gets or sets the current implementation of <see cref="AutoRegistrationList"/>
        /// that the application will use.  If null, an empty <see cref="AutoRegistrationList"/> is returned.
        /// </summary>
        public AutoRegistrationList AutoRegistrations { get; set; }

        /// <summary>
        /// Gets or sets the current implementation of <see cref="IServiceLocator"/>.
        /// </summary>
        public IServiceLocator ServiceLocator {
            get { return Application.ServiceLocator; }
        }

        /// <summary>
        /// Gets or sets the current instance of <see cref="TurbineApplication"/>.
        /// </summary>
        public ITurbineApplication Application { get; private set; }

        #region IDisposable Members

        /// <summary>
        /// Cleans up the current <see cref="IServiceLocator"/> associated with the context.
        /// </summary>
        public virtual void Dispose() {
            BladeList components = GetAllBlades();

            if (components != null) {
                foreach (IBlade component in components) {
                    component.Dispose();
                }
            }

            if (ServiceLocator != null) {
                ServiceLocator.Dispose();
            }
        }

        #endregion

        /// <summary>
        /// Initializes the current context by auto-registering the default components.
        /// </summary>
        public virtual void Initialize(ITurbineApplication application) {
            InitializeComponents();
        }

        /// <summary>
        /// Initializes the registered <see cref="Blade"/> instances.
        /// </summary>
        protected virtual void InitializeComponents() {
            BladeList components = GetAllBlades();
            if (components == null)
                return;

            foreach (IBlade component in components) {
                component.Initialize(this);
            }
        }

        /// <summary>
        /// Executes the current context.
        /// </summary>
        public virtual void Turn() {
            CoreRegistrationSetup();
            ProcessAutomaticRegistration();
            ProcessManualRegistrations();

            AutoRegistrationSetup();
            ProcessAutomaticRegistration();

            RunBlades();
        }

        /// <summary>
        /// Executes the registered <see cref="Blade"/> instances.
        /// </summary>
        protected virtual void RunBlades() {
            BladeList components = GetAllBlades();
            if (components == null) return;

            foreach (IBlade component in components) {
                component.Spin(this);
            }
        }

        /// <summary>
        /// Sets up all the instances of <see cref="IServiceRegistration"/>, <see cref="IRouteRegistrator"/>, 
        /// <see cref="IController"/> that the application uses.
        /// </summary>
        protected virtual void AutoRegistrationSetup() {
            BladeList components = GetAllBlades();
            if (components == null) return;

            foreach (IBlade component in components) {
                var autoRegistration = component as ISupportAutoRegistration;
                if (autoRegistration == null) return;

                autoRegistration.AddRegistrations(AutoRegistrations);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void CoreRegistrationSetup() {
            AutoRegistrations
                .Add(Registration.Simple<IServiceRegistration>())
                .Add(Registration.Simple<IBlade>());
        }

        /// <summary>
        /// Gets the list of components that are to be used for the application.
        /// </summary>
        /// <returns>A list of the components registered with the application.</returns>
        public virtual BladeList GetAllBlades() {
            var list = new BladeList(CoreBlades.GetBlades());

            BladeList commonBlades = GetCommonBlades();
            if (commonBlades != null) {
                list.AddRange(commonBlades);
            }

            return list;
        }

        /// <summary>
        /// Gets all the registered <see cref="IBlade"/> instances that are not part of the <see cref="CoreBlades"/> list.
        /// </summary>
        /// <returns></returns>
        public virtual BladeList GetCommonBlades() {
            List<IBlade> resolvedComponents = (from blade in ServiceLocator.ResolveServices<IBlade>()
                                               where !BladeExtensions.IsCoreBlade(blade)
                                               select blade).ToList();

            return new BladeList(resolvedComponents);
        }

        /// <summary>
        /// Iterates through all the registered <see cref="IServiceRegistration"/> instances
        /// </summary>
        protected virtual void ProcessManualRegistrations() {
            IList<IServiceRegistration> registrationList = ServiceLocator.ResolveServices<IServiceRegistration>();
            if (registrationList == null || registrationList.Count == 0) return;

            foreach (IServiceRegistration reg in registrationList) {
                reg.Register(ServiceLocator);
            }
        }

        /// <summary>
        /// Iterates through all the <see cref="ServiceRegistration"/> instances within 
        /// the <see cref="AutoRegistrations"/> table 
        /// and process them with the registered <see cref="IAutoRegistrator"/> instance or 
        /// uses <see cref="DefaultAutoRegistrator"/> if one is not registered.
        /// </summary>
        protected virtual void ProcessAutomaticRegistration() {
            IAutoRegistrator autoRegistrator = GetAutoRegistrator();

            AutoRegistrationList registrationList = AutoRegistrations;

            foreach (ServiceRegistration registration in registrationList) {
                autoRegistrator.AutoRegister(registration);
            }

            AutoRegistrations.Clear();
        }

        /// <summary>
        /// Gets the <seealso cref="IAutoRegistrator"/> to use. Checks the container first, it one not found, it returns
        /// the default one.
        /// </summary>
        /// <returns></returns>
        protected virtual IAutoRegistrator GetAutoRegistrator() {
            try {
                return ServiceLocator.Resolve<IAutoRegistrator>();
            } catch (ServiceResolutionException) {
                return new DefaultAutoRegistrator(ServiceLocator);
            }
        }
    }
}
